﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Threading;
using Red.Core;

namespace Red.Controls
{
    /// <summary>
    /// Interaction logic for Searcher.xaml
    /// </summary>
    public partial class Searcher : UserControl
    {
        #region Data

        private ICollectionView _itemsView;
        private readonly DispatcherTimer _dispatcherTimer = new DispatcherTimer();
        private const int _minTextLength = 2;

        #endregion Data

        #region Properties

        public static readonly DependencyProperty DelayProperty = DependencyProperty.Register( "Delay", typeof( int ), typeof( Searcher ), new PropertyMetadata( OnDelayPropertyChanged ) );

        public int Delay
        {
            get { return ( int )GetValue( DelayProperty ); }
            set { SetValue( DelayProperty, value ); }            
        }

        private static void OnDelayPropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
        {
            var searcher = ( Searcher )d;
            var delay = ( int )e.NewValue;
            searcher._dispatcherTimer.Interval = new TimeSpan( 0, 0, 0, 0, delay );
        }

        /// <summary>
        /// List of items that will be displayed in Searcher control.
        /// </summary>
        public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register( "Items", typeof( IEnumerable<SearchItem> ), typeof( Searcher ), new PropertyMetadata( OnItemsPropertyChanged ) );

        public IEnumerable<SearchItem> Items
        {
            get { return ( IEnumerable<SearchItem> )GetValue( ItemsProperty ); }
            set { SetValue( ItemsProperty, value ); }
        }

        private static void OnItemsPropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
        {
            var searcher = ( Searcher )d;
            searcher.RecalculateSimilarity();
            searcher.SetupItemsView();
        }

        public static readonly DependencyProperty SelectedItemProperty = DependencyProperty.Register( "SelectedItem", typeof( SearchItem ), typeof( Searcher ) );

        public SearchItem SelectedItem
        {
            get { return ( SearchItem )GetValue( SelectedItemProperty ); }
            set { SetValue( SelectedItemProperty, value ); }
        }

        public static readonly DependencyProperty SearchTextProperty = DependencyProperty.Register( "SearchText", typeof( string ), typeof( Searcher ), new PropertyMetadata( OnSearchTextPropertyChanged ) );

        public string SearchText
        {
            get { return ( string )GetValue( SearchTextProperty ); }
            set { SetValue( SearchTextProperty, value ); }
        }

        private static void OnSearchTextPropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
        {
            var searcher = ( Searcher )d;
            var searchText = ( string )e.NewValue;

            if ( searchText.Length > _minTextLength || string.IsNullOrEmpty( searchText ) )
                searcher._dispatcherTimer.Start();
        }

        #endregion Properties

        #region C-tor

        public Searcher()
        {
            InitializeComponent();
            DataContext = this;
            _dispatcherTimer.Tick += ( o, args ) =>
            {
                _dispatcherTimer.Stop();
                RecalculateSimilarity();
                _itemsView.Refresh();
                _itemsListBox.SelectedIndex = 0;
            };
        }

        #endregion C-tor

        #region Private methods

        private void SetupItemsView()
        {
            _itemsView = CollectionViewSource.GetDefaultView( Items );
            _itemsView.SortDescriptions.Add( new SortDescription( "Similarity", ListSortDirection.Descending ) );
            _itemsView.Filter = item =>
            {
                var searchItem = item as SearchItem;
                return searchItem.Similarity >= 0;
            };
        }

        private void RecalculateSimilarity()
        {
            foreach ( var item in Items )
                item.Similarity = 0;

            if ( !string.IsNullOrEmpty( SearchText ) )
            {
                foreach ( var item in Items )
                {
                    var score = 0;
                    var searchTextLowered = SearchText.ToLower();
                    var itemLowered = item.Title.ToLower();

                    var searchList = new List<string> { searchTextLowered };
                    searchList.AddRange( searchTextLowered.Split( new[] { ' ', '_' } ) );
                    var itemsList = new List<string> { itemLowered };
                    itemsList.AddRange( itemLowered.Split( new[] { ' ', '_' } ) );

                    if ( searchTextLowered == itemLowered )
                        score += 100;

                    if ( itemLowered.Contains( searchTextLowered ) )
                        score += 50;

                    foreach ( var listItem in itemsList )
                    {
                        foreach ( var searchItem in searchList )
                        {
                            if ( listItem == searchItem )
                                score += 50;

                            if ( listItem.Contains( searchItem ) )
                                score += 25;

                            var distance = SearchUtils.ComputeDistance( listItem, searchItem );
                            if ( 0 <= distance && distance <= 2 )
                            {
                                score += 50 - distance * 5;
                            }
                            else
                            {
                                score -= distance;
                            }
                        }
                    }
                    item.Similarity = score;
                }
            }
        }

        #endregion Private methods
    }

    public sealed class SearchItem : ViewModel
    {
        #region Data

        private int _similarity;

        #endregion Data

        #region Properties

        public object Data { get; private set; }

        public string Title { get; private set; }
        
        public int Similarity
        {
            get { return _similarity; }
            set
            {
                if ( value != _similarity )
                {
                    _similarity = value;
                    OnPropertyChanged();
                }
            }
        }

        #endregion Properties

        #region C-tor

        public SearchItem( string title, object obj )
        {
            Title = title;
            Data = obj;
        }

        #endregion C-tor
    }
}
